#!/usr/bin/python
# sample application drawing a ball such as in Sensor Sample
import time, StringIO
from Tkinter import *
import Image, ImageDraw, ImageTk
from Sensor import SensorReceiver
from ImgDist import ImgSenderSocket, ImgSenderHTTP
import thread


class DrawCanvas(object):
    """Canvas, center at 0,0; left lower at -1,-1, right upper at 1,1
    Use PIL image to draw in order to make 'screenshots', ugly/slow ..."""

    def __init__(self, canvas):
        self.canvas = canvas
        self.width = int(canvas.cget("width"))
        self.height = int(canvas.cget("height"))
        self.shift_right = self.width/2
        self.shift_down = self.height/2 
        self.scale_x = self.width/2
        self.scale_y = -self.height/2 # flips as well
        self.img = Image.new("RGB", (self.width, self.height), "#FFFFFF")
        self.img_lock = thread.allocate_lock()
        self.draw = ImageDraw.Draw(self.img)
        self.pi_img = ImageTk.PhotoImage(self.img)
        self.canvas.create_image((0, 0), image=self.pi_img, anchor=NW)

    def draw_ball(self, x_pos, y_pos, radius, 
                  fill="#0000FF", fillneg="#FF0000"):
        """draw a new ball with axis at x,y with radius r and fill color fill, 
        on negative radius use absolute radius but different fill color"""
        x_pos, y_pos = self._translate_xy(x_pos, y_pos)
        fill = fill if radius >= 0 else fillneg
        radius = radius if radius >= 0 else -radius
        radius_x, _ = self._scale_xy(radius, radius)
        self.img_lock.acquire()
        self.draw.rectangle((0, 0, self.width, self.height), 
                            fill="#FFFFFF") # clear
        self._draw_axis()
        self.draw.ellipse((x_pos-radius_x, y_pos-radius_x,
                           x_pos+radius_x, y_pos+radius_x), 
                          fill=fill)
        self.img_lock.release()

    def _draw_axis(self):
        "two black lines intersecting in 0,0"
        x_0, y_0 = self._translate_xy(-1, 0)
        x_1, y_1 = self._translate_xy(1, 0)
        self.draw.line((x_0, y_0, x_1, y_1), 
                       fill="#000000")
        x_0, y_0 = self._translate_xy(0, -1)
        x_1, y_1 = self._translate_xy(0, 1)
        self.draw.line((x_0, y_0, x_1, y_1), 
                       fill="#000000")

    def _translate_xy(self, x_pos, y_pos):
        "translate from -1 <= x,y <= 1 to pixel coordinates"
        x_pos = x_pos*self.scale_x + self.shift_right
        y_pos = y_pos*self.scale_y + self.shift_down
        return (x_pos, y_pos)

    def _scale_xy(self, x_pos, y_pos):
        "only scale but no movement of origin"
        return (x_pos*self.scale_x, y_pos*self.scale_y)

    def flip(self):
        "push image to the canvas"
        self.pi_img.paste(self.img, None)

    def screenshot(self):
        "take a screenshot of a complete picture"
        sio = StringIO.StringIO()
        self.img_lock.acquire()
        self.img.save(sio, format="JPEG", optimize=1)
        self.img_lock.release()
        return sio.getvalue()


class Application(Frame):
    """Simple Tkinter app showing a ball representing sensor values
    where -1 <= x,y,z <= 1 should hold while x,y are respective 
    canvas coordinates and z is the radius of the ball."""

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.grid()
        self.create_widgets()
        # sensor specific 
        self.sens_receiver = SensorReceiver() # instance of receiver
        self.sens_receiver.register(self.draw_ball) # register callback
        # Do not forget to call self.sens_receiver.close() at the end
        # end sensor specific
        self.sender_socket = None # initially no sender
        self.sender_http = None
        # at least 0.2 seconds between two sends, retrieving won't be
        # faster anyway
        self.sender_min_dist = 0.2
        self.sender_last_sent = 0.0
        self._root().protocol("WM_DELETE_WINDOW", self.end_it)
        self.reset()

    def create_widgets(self):
        "make the ui"
        self.canvas = Canvas(self, width=240, height=320)
        self.canvas.grid(column=0, row=0)
        self.dcanvas = DrawCanvas(self.canvas)
        self.button_frame = Frame(self)
        self.button_frame.grid(column=1, row=0, sticky=N+E+W)
        self.quit_button = Button(self.button_frame, 
                                  text="Quit",
                                  command=self.end_it)
        self.quit_button.pack(fill=BOTH)
        self.reset_button = Button(self.button_frame, 
                                   text="Reset", 
                                   command=self.reset)
        self.reset_button.pack(fill=BOTH)
        self.accept = IntVar()
        self.accept_button = Checkbutton(self.button_frame, 
                                         text="Accept Packages",
                                         variable=self.accept)
        self.accept_button.pack(anchor=W)
        self.accept.set(1)
        self.dist_http = IntVar()
        self.dist_http_button = Checkbutton(self.button_frame, 
                                            text="DistHTTP Images",
                                            variable=self.dist_http)
        self.dist_http_button.pack(anchor=W)
        self.dist_http.set(0)
        self.dist_socket = IntVar()
        self.dist_socket_button = Checkbutton(self.button_frame, 
                                              text="DistSocket Images",
                                              variable=self.dist_socket)
        self.dist_socket_button.pack(anchor=W)
        self.dist_socket.set(0)
        self.display_frame = Frame(self)
        self.display_frame.grid(column=0, columnspan=2, row=1)
        self.x_label = Label(self.display_frame, text="X: ", anchor=W, width=12)
        self.x_label.pack(side=LEFT)
        self.y_label = Label(self.display_frame, text="Y: ", anchor=W, width=12)
        self.y_label.pack(side=LEFT)
        self.z_label = Label(self.display_frame, text="Z: ", anchor=W, width=12)
        self.z_label.pack(side=LEFT)

    def draw_ball(self, dic):
        "draw the ball based on the numbers in dic"
        if not self.accept.get():
            return
        x_pos, y_pos, z_pos = dic["x"], dic["y"], dic["z"]
        z_pos += 1.0 # offset gravity
        self.dcanvas.draw_ball(x_pos, y_pos, z_pos)
        self.dcanvas.flip()
        self.canvas.update()
        self.x_label["text"] = "X: %g " % x_pos
        self.y_label["text"] = "Y: %g " % y_pos
        self.z_label["text"] = "Z: %g " % z_pos
        if time.time() - self.sender_last_sent < self.sender_min_dist:
            return # don't send
        if self.dist_socket.get():
            if not self.sender_socket:
                try:
                    self.sender_socket = ImgSenderSocket()
                except Exception, data:
                    print "nope, init socket sending didn't work", data
                    self.dist_socket.set(0)
                    self.sender_socket = None
            if self.sender_socket:
                try:
                    self.sender_socket(self.dcanvas.screenshot())
                    self.sender_last_sent = time.time()
                except Exception, data:
                    print "nope, socket sending didn't work", data
                    self.dist_socket.set(0)
                    self.sender_socket = None
        if self.dist_http.get():
            if not self.sender_http:
                try:
                    self.sender_http = ImgSenderHTTP()
                except Exception, data:
                    print "nope, init HTTP sending didn't work", data
                    self.dist_http.set(0)
                    self.sender_http = None
            if self.sender_http:
                try:
                    self.sender_http(self.dcanvas.screenshot())
                    self.sender_last_sent = time.time()
                except Exception, data:
                    print "nope, HTTP sending didn't work", data
                    self.dist_http.set(0)
                    self.sender_http = None                    

    def reset(self):
        "reset the ball to centrum"
        self.draw_ball({"x":0, "y":0, "z":-.75})

    def end_it(self):
        "at the end of the application"
        self.accept.set(0)
        self.sens_receiver.close()
        if self.sender_socket:
            self.sender_socket.close()
        self.quit()

if __name__ == '__main__':
    app = Application()
    app.master.title("Show Sensor Ball")
    print "draws a ball as is done on the phone itself"
    print "start TkSID.py and select (only) the respective "
    print "'normalize' filter of your phone"
    try:
        app.mainloop()
    except KeyboardInterrupt:
        app.end_it()
